#pragma once

#include "Protocol.h"
#include "RUDP.h"
#include "Header.h"

#ifdef _WIN32
#pragma comment(lib,"ws2_32.lib")
#endif

class Replication
{
	//	Game Protocol
private:
	static const unsigned char GainControlID_MSG = 1;
	static const unsigned char Entry_MSG = 2;
	static const unsigned char Ready2Play_MSG = 3;
	static const unsigned char Controll_MSG = 4;

	//	RUDP Protocol
	static const unsigned char RUDP_MSG = 0xff;

	//	Properties
private:
	static const unsigned int RBuffLen = 1024 * 1024;
	static const unsigned int WBuffLen = 1024 * 1024;
	static const unsigned int FPS = 1000 / 42;

	static const unsigned int MaxMatchTime = 3 * 1000;
	static const unsigned int MaxWaitPlayerEntryTime = 30 * 1000;
	static const unsigned int MaxWaitInputTime = 8 * 1000;

	static const unsigned int MaxPendingSendCount = 40;

private:
	struct RUDPINFO
	{
		RUDP *RUDPSession;
		sockaddr *endpoint;
	};

public:
	Replication(short port);
	~Replication();
	void Run();

private:
	void OnEntry(char *ptr, sockaddr *fromAddr);
	void OnReady(char *ptr, sockaddr *fromAddr);
	void OnControll(char *ptr, sockaddr *fromAddr);
	void RudpUpdate();
	void MatchUpdate();
	void Synchronization();
	void RemoveRoom(std::map<uint16_t, PlayerController *>::iterator &iter);
	void InitializeMatchingPlayer(PlayerMatching &player, sockaddr *from_addr);
	void EnterRoom(std::map<uint8_t, std::map<uint32_t, PlayerMatching> *>::iterator &matchedRoom);
	uint16_t RandomID();
	uint32_t HashCode(sockaddr *addr);
	uint64_t Clock();

private:
	uint32_t syncTimer;
	uint16_t roomId;
	int socket_fd;
	char *recvBuffer;
	char *sendBuffer;
	sockaddr from;
	socklen_t fromAddrLength;
	std::random_device rd;
	std::mt19937 mt;
	std::uniform_int_distribution<uint16_t> dis;
	std::map<uint32_t, RUDPINFO> RudpSession;
	std::queue<RUDP::UserData *> RudpRecvQueue;
	std::map<uint8_t, std::map<uint32_t, PlayerMatching> *> EntryPlayers;
	std::map<uint16_t, PlayerController *> SyncPlayerControlls;
};